package mr.ali.video.web.service;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mr.ali.video.web.config.TencentCosConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 腾讯云COS服务类
 */
@Service
public class TencentCosService {

    private static final Logger logger = LoggerFactory.getLogger(TencentCosService.class);

    @Autowired
    private TencentCosConfig cosConfig;

    /**
     * 获取COSClient实例
     *
     * @return COSClient实例
     */
    private COSClient getCosClient() {
        // 初始化用户身份信息
        COSCredentials cred = new BasicCOSCredentials(cosConfig.getSecretId(), cosConfig.getSecretKey());
        // 设置bucket的区域
        ClientConfig clientConfig = new ClientConfig(new Region(cosConfig.getRegion()));
        // 生成cos客户端
        return new COSClient(cred, clientConfig);
    }

    /**
     * 通过文件名获取文件内容和地址
     *
     * @param fileName 文件名（对象键）
     * @return 包含文件内容和地址的Map
     */
    public Map<String, Object> getFileContentAndUrl(String fileName) {
        COSClient cosClient = null;
        Map<String, Object> result = new HashMap<>();

        try {
            cosClient = getCosClient();

            // 获取文件内容
            GetObjectRequest getObjectRequest = new GetObjectRequest(cosConfig.getBucketName(), fileName);
            COSObject cosObject = cosClient.getObject(getObjectRequest);
            COSObjectInputStream cosObjectInput = cosObject.getObjectContent();

            // 读取文件内容
            byte[] content = readInputStream(cosObjectInput);
            result.put("content", content);

            // 获取文件元数据
            ObjectMetadata metadata = cosObject.getObjectMetadata();
            result.put("contentType", metadata.getContentType());
            result.put("contentLength", metadata.getContentLength());
            result.put("lastModified", metadata.getLastModified());

            // 生成文件的临时访问URL（有效期1小时）
            Date expirationDate = new Date(System.currentTimeMillis() + 3600 * 1000);
            URL url = cosClient.generatePresignedUrl(cosConfig.getBucketName(), fileName, expirationDate, HttpMethodName.GET);
            result.put("url", url.toString());

            logger.info("成功获取文件 {} 的内容和地址", fileName);

        } catch (CosServiceException e) {
            logger.error("获取文件失败，COS服务异常: {}", e.getErrorMessage());
            throw new RuntimeException("获取文件失败，COS服务异常: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            logger.error("获取文件失败，COS客户端异常: {}", e.getMessage());
            throw new RuntimeException("获取文件失败，COS客户端异常: " + e.getMessage(), e);
        } catch (IOException e) {
            logger.error("读取文件内容失败: {}", e.getMessage());
            throw new RuntimeException("读取文件内容失败: " + e.getMessage(), e);
        } finally {
            // 关闭客户端
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }

        return result;
    }

    /**
     * 通过文件名获取文件的临时访问URL
     *
     * @param fileName 文件名（对象键）
     * @param expireTimeInSeconds URL有效期（秒）
     * @return 文件的临时访问URL
     */
    public String getFileUrl(String fileName, long expireTimeInSeconds) {
        COSClient cosClient = null;

        try {
            cosClient = getCosClient();

            // 设置URL过期时间
            Date expirationDate = new Date(System.currentTimeMillis() + expireTimeInSeconds * 1000);

            // 生成临时访问URL
            URL url = cosClient.generatePresignedUrl(cosConfig.getBucketName(), fileName, expirationDate, HttpMethodName.GET);

            logger.info("成功生成文件 {} 的临时访问URL", fileName);
            return url.toString();

        } catch (CosServiceException e) {
            logger.error("生成临时访问URL失败，COS服务异常: {}", e.getErrorMessage());
            throw new RuntimeException("生成临时访问URL失败，COS服务异常: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            logger.error("生成临时访问URL失败，COS客户端异常: {}", e.getMessage());
            throw new RuntimeException("生成临时访问URL失败，COS客户端异常: " + e.getMessage(), e);
        } finally {
            // 关闭客户端
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }
    }

    /**
     * 检查文件是否存在
     *
     * @param fileName 文件名（对象键）
     * @return 文件是否存在
     */
    public boolean doesFileExist(String fileName) {
        COSClient cosClient = null;

        try {
            cosClient = getCosClient();
            return cosClient.doesObjectExist(cosConfig.getBucketName(), fileName);
        } catch (CosClientException e) {
            logger.error("检查文件是否存在失败: {}", e.getMessage());
            throw new RuntimeException("检查文件是否存在失败: " + e.getMessage(), e);
        } finally {
            // 关闭客户端
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }
    }

    /**
     * 读取输入流内容
     *
     * @param inputStream 输入流
     * @return 字节数组
     * @throws IOException IO异常
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;

        try {
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            return outputStream.toByteArray();
        } finally {
            try {
                inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                logger.error("关闭流失败: {}", e.getMessage());
            }
        }
    }

    /**
     * 列出指定文件夹内的所有文件
     *
     * @param folderPath 文件夹路径（以/结尾）
     * @return 文件信息列表
     */
    public List<Map<String, Object>> listFilesInFolder(String folderPath) {
        COSClient cosClient = null;
        List<Map<String, Object>> fileList = new ArrayList<>();

        try {
            cosClient = getCosClient();

            // 确保文件夹路径以/结尾
            if (!folderPath.endsWith("/")) {
                folderPath = folderPath + "/";
            }

            // 创建列出对象请求
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
            listObjectsRequest.setBucketName(cosConfig.getBucketName());
            listObjectsRequest.setPrefix(folderPath);
            listObjectsRequest.setDelimiter("/"); // 使用分隔符来模拟文件夹结构

            // 列出对象
            ObjectListing objectListing;
            do {
                objectListing = cosClient.listObjects(listObjectsRequest);

                // 处理文件对象
                for (COSObjectSummary cosObjectSummary : objectListing.getObjectSummaries()) {
                    // 跳过文件夹本身
                    if (cosObjectSummary.getKey().equals(folderPath)) {
                        continue;
                    }

                    Map<String, Object> fileInfo = new HashMap<>();
                    fileInfo.put("key", cosObjectSummary.getKey());
                    fileInfo.put("size", cosObjectSummary.getSize());
                    fileInfo.put("lastModified", cosObjectSummary.getLastModified());
                    fileInfo.put("storageClass", cosObjectSummary.getStorageClass());

                    // 获取CDN加速地址
                    fileInfo.put("cdnUrl", getCdnUrl(cosObjectSummary.getKey()));

                    fileList.add(fileInfo);
                }

                // 设置下一个标记，继续列出对象
                listObjectsRequest.setMarker(objectListing.getNextMarker());
            } while (objectListing.isTruncated());

            logger.info("成功列出文件夹 {} 中的文件，共 {} 个文件", folderPath, fileList.size());

        } catch (CosServiceException e) {
            logger.error("列出文件夹内容失败，COS服务异常: {}", e.getErrorMessage());
            throw new RuntimeException("列出文件夹内容失败，COS服务异常: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            logger.error("列出文件夹内容失败，COS客户端异常: {}", e.getMessage());
            throw new RuntimeException("列出文件夹内容失败，COS客户端异常: " + e.getMessage(), e);
        } finally {
            // 关闭客户端
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }

        return fileList;
    }

    /**
     * 获取文件的CDN加速地址
     *
     * @param objectKey 对象键（文件路径）
     * @return CDN加速地址
     */
    public String getCdnUrl(String objectKey) {
        // 使用自定义加速域名
        return String.format("https://app.erkantv.cn/%s", objectKey);
    }

    /**
     * 获取临时密钥
     * @return 包含临时密钥信息的Map
     */
    public Map<String, Object> getTempSecretKey() {
        Map<String, Object> credentials = new HashMap<>();

        try {
            // 使用永久密钥生成临时密钥
            // 这里使用的是简化版的临时密钥生成方式，实际生产环境中应该使用STS服务

            // 创建一个随机的会话名称
            String sessionName = "session-" + UUID.randomUUID().toString().replace("-", "");

            // 设置临时密钥的有效期（30分钟）
            long startTime = System.currentTimeMillis() / 1000;
            long expiredTime = startTime + 1800;

            // 构建临时密钥信息
            credentials.put("TmpSecretId", cosConfig.getSecretId());
            credentials.put("TmpSecretKey", cosConfig.getSecretKey());
            credentials.put("SecurityToken", ""); // 在实际生产环境中，这应该是从STS服务获取的安全令牌
            credentials.put("StartTime", startTime);
            credentials.put("ExpiredTime", expiredTime);
            credentials.put("Region", cosConfig.getRegion());
            credentials.put("Bucket", cosConfig.getBucketName());

            logger.info("成功生成临时密钥");
            return credentials;
        } catch (Exception e) {
            logger.error("获取临时密钥失败: {}", e.getMessage());
            throw new RuntimeException("获取临时密钥失败", e);
        }
    }
}
